This document written by SPennLUE (spennini ;at; nyit.edu) (c)2009-2010. Do not distribute unless unaltered.
Website: SPennLUE Projects


Introduction

Unlike ROMs, PC games might not all use the same formats and file types. As such, there is no single jack-of-all-trades method of patching games. ROMs are a different story; they are made of just a single file, and formats such as IPS and NINJA are available to make things super-easy. The changes made when hacking or translating a PC game, however, usually apply to multiple files.

In this document, you will learn how to create patches for individual files, then write a simple script to compile them into a single easy-to-use program. This guide uses excerpts from my Rockman2 Neta translation patcher. The relevant parts are copied here, but if you'd like to see the whole script, you can view it here.

You will need the following:

*The game for which you are creating the patch
You will need both the original version and the one you have edited (this document does not cover file editing).

*GenPat
Creates the patch files.

*Text editor
Regular old Notepad is fine for typing up your script. Don't use Microsoft Word or anything that adds extra formatting.

*Nullsoft Scriptable Install System (NSIS)
Compiles your script. ("compile" is another word for taking a script and turning it into a program)

*Practice and patience
This is usually how it's done. Not too long ago, I didn't know a single thing written in this document. I learned it all by asking on forums and by playing around with tutorials.


Creating the patches

First thing's first. You'll need the GenPat program linked above. Wherever you save it, make sure to put your altered AND unaltered game files in there as well. You won't need every single file the game uses, only the ones you have changed. Make sure to place them directly in GenPat's folder. For convenience, you can rename them to make their names shorter, but this is optional.

GenPat runs in the command prompt, so we have to type our commands in there. Go into your Start menu, click "Run...", type CMD in the box, and hit Enter. Do you remember where you saved GenPat.exe? If you saved it in c:\genpat then type in the following (GenPat and the command prompt are not case-sensitive, so you don't have to use capital letters) and hit Enter:

CD C:\GENPAT

Now we're in the folder with GenPat, and we can create patches. For this example, let's say our files are called old.exe and new.exe (GenPat can make patches for any kind of file, not just EXE files). All you have to do is type the following and hit Enter:

GENPAT.EXE OLD.EXE NEW.EXE MY_FREAKING_AWESOME_PATCH.PAT

That last file is your patch file. Name it anything you like, as long as it has the .pat extension. If you want your prograjm to change several different files, you will need to create a patch for each one. Be sure to give them names you will remember, otherwise you won't know which is which. Keeping things organized now will save you a lot of trouble later.

When you enter that command, the command prompt will quickly show roughly 10 lines of text explaining what the program did. The specifics aren't important; as long as you don't see an error, the patch should have been created successfully.

That's all there is to creating patches. Just do that for each altered file in your game.


Writing the script

The "script" is the list of actions your patcher program will take. I'll lay out most of the work for you, but you will still need to know how to apply it for your program. Note that this is a pretty simplistic script that probably could be improved in a few places, but I wanted to keep it simple and easy to understand. If you're interested in creating more elaborate and/or efficient code, check out the NSIS wiki.

Create a fresh new text document and type this in (just like GenPat, NSIS is not case-sensitive, so it doesn't matter if you use capital or lower-case letters):

Name "[1]"

outfile "[2]"

installdir $instdir

dirtext "[3]"

showinstdetails show

In place of [1], type in the name you want to appear in the title bar when the program is running. For [2], put in what you want to name the patcher program. Make sure it ends in .exe or it won't work. Remember, we're creating an EXE, so that's what it has to be! Finally, [3] is the message that appears in the first window that appears when you run the program. Make sure to put quotes around these messages, just like you see above.

Next, we will need a specific macro. Macros are for automating certain tasks, but you don't really need to know how it works in order to use it. Just copy the following text beneath those first five lines:

;%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
; MoveFile and MoveFolder macros
;
; Author:  theblazingangel@aol.com (for the AutoPatcher project - www.autopatcher.com)
; Created: June 2007 
;%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 
;==================
; MoveFile macro
;==================
 
    !macro MoveFile sourceFile destinationFile
 
    !define MOVEFILE_JUMP ${__LINE__}
 
    ; Check source actually exists
 
        IfFileExists "${sourceFile}" +3 0
        SetErrors
        goto done_${MOVEFILE_JUMP}
 
    ; Add message to details-view/install-log
 
        DetailPrint "Moving/renaming file: ${sourceFile} to ${destinationFile}"
 
    ; If destination does not already exists simply move file
 
        IfFileExists "${destinationFile}" +3 0
        rename "${sourceFile}" "${destinationFile}"
        goto done_${MOVEFILE_JUMP}
 
    ; If destination already exists
 
        Push $R0
        Push $R1
        Push $R2
        push $R3
 
        GetFileTime "${sourceFile}" $R0 $R1
        GetFileTime "${destinationFile}" $R2 $R3
 
        IntCmp $R0 $R2 0 older_${MOVEFILE_JUMP} newer_${MOVEFILE_JUMP}
        IntCmp $R1 $R3 older_${MOVEFILE_JUMP} older_${MOVEFILE_JUMP} newer_${MOVEFILE_JUMP}
 
        older_${MOVEFILE_JUMP}:
        delete "${sourceFile}"
        goto time_check_done_${MOVEFILE_JUMP}
 
        newer_${MOVEFILE_JUMP}:
        delete "${destinationFile}"
        rename "${sourceFile}" "${destinationFile}"
        delete "${sourceFile}" ;incase above failed!
 
        time_check_done_${MOVEFILE_JUMP}:
 
        Pop $R3
        Pop $R2
        Pop $R1
        Pop $R0
 
    done_${MOVEFILE_JUMP}:
 
    !undef MOVEFILE_JUMP
 
    !macroend

This chunk of code creates some of the commands we will be using in our script, so it must be included.

There's one more thing we need to put in before we can get to the good part.

section "apply patches"

    !include 'FileFunc.nsh'
 
    Var /GLOBAL switch_overwrite
    StrCpy $switch_overwrite 0
    !include 'MoveFileFolder.nsh'

Don't worry about what any of this stuff does. Just remember that all this stuff has to be in your script in the order I've listed it.

Now we can do what we need. Put your patch files in the same folder as your script file. For each file that needs to be changed, we will add a section of code that looks something like this:

    setoutpath $instdir

    !insertmacro MoveFile "$INSTDIR\[1]" "$INSTDIR\[2]"

    initpluginsdir
    file /oname=$pluginsdir\[3] [3]

    detailprint "Translating [1]..."
    vpatch::vpatchfile "$pluginsdir\[3]" "$instdir\[2]" "$instdir\[1]"

    pop $r0
    detailprint "Result: $r0"

    Delete "$instdir\[2]"
    detailprint "Discarding old file..."

    Pop $R0
    detailprint "Result: $r0"

Once again, there are a few things you need to fill in. [1] is the original unaltered file's name (don't forget the extension). [2] can be anything, as long as it has the same file extension as [1]. [3] is the name of the patch file being used on [1], so make sure you use the right patch on the right program (of course, it must end in .pat or your script won't be able to find it).

In this example, I will use a patch that translates a game's readme file.

    setoutpath $instdir

    !insertmacro MoveFile "$INSTDIR\readme.txt" "$INSTDIR\jpreadme.txt"

    initpluginsdir
    file /oname=$pluginsdir\readme.pat readme.pat

    detailprint "Translating readme.txt..."
    vpatch::vpatchfile "$pluginsdir\readme.pat" "$instdir\jpreadme.txt" "$instdir\ReadMe.txt"

    pop $r0
    detailprint "Result: $r0"

    Delete "$instdir\jpreadme.txt"
    detailprint "Discarding old file..."

    Pop $R0
    detailprint "Result: $r0"

Some of the messages here can be customized, if you want. "Translating [1]...", "Result: ", and "Discarding old file..." are just what I used for my program, but feel free to use something more fitting if you like. The "$r0" is part of the macro we borrowed, and it will actually say something more meaningful depending on the status of that particular action.

Make sure to leave the original files in their proper folders. Rockman2 Neta, for example, keeps its images in a "subfolder" (subfolders are folders located within other folders) called \graphic\. Our patching program shouldn't move files around while patching them, or the game might not work. This is true for many other games as well. Our program must patch the file without moving it.

If Game.exe is in c:\program files\game then you can leave [1] just as it is. But let's say we want to patch a file that is normally in a subfolder. If Image.bmp is in c:\program files\game\folder2 you will need to include that extra folder in [1]. So, "$INSTDIR\[1]" and "$INSTDIR\[2]" would need to be "$INSTDIR\folder2\[1]" and "$INSTDIR\folder2\[2]" for the section of our script which acts on that image's patch file. This will let us patch the image without moving it.

In this example, I will patch an image which is normally located within a subfolder.

    setoutpath $instdir

    !insertmacro MoveFile "$INSTDIR\graphic\Font02.bmp" "$INSTDIR\graphic\jpFont02.bmp"

    initpluginsdir
    file /oname=$pluginsdir\font2bmp.pat font2bmp.pat

    detailprint "Translating Font02.bmp..."
    vpatch::vpatchfile "$pluginsdir\font2bmp.pat" "$instdir\graphic\jpFont02.bmp" "$instdir\graphic\Font02.bmp"

    pop $r0
    detailprint "Result: $r0"

    Delete "$instdir\graphic\jpFont02.bmp"
    detailprint "Discarding old file..."
    Pop $R0
    detailprint "Result: $r0"


Renaming files while patching them (optional)

It is highly recommended that you do not change any of the files' names, otherwise the program might not work properly. However, the main EXE (the one you double-click to start the program) is usually safe to rename. If you want your program to change the EXE's name, you can leave out the line that begins with !insertmacro for just the one section which is responsible for patching the EXE.

The code used for this is very similar to the code we have been using all along:

    setoutpath $instdir

    initpluginsdir
    file /oname=$pluginsdir\[3] [3]

    detailprint "Translating [1]..."
    vpatch::vpatchfile "$pluginsdir\[3]" "$instdir\[1]" "$instdir\[2]"

    pop $r0
    detailprint "Result: $r0"

    Delete "$instdir\[1]"
    detailprint "Discarding old file..."

    Pop $R0
    detailprint "Result: $r0"

Here, [1] is the original EXE, [2] is the newly renamed and altered EXE, and [3] is the patch file applied to the EXE. Even though we are free to rename [2], make sure not to change the file extension.


At the end of your script, add the following line:

sectionend

Now save your script as a .nsi file. You'll see why in a minute.


Compiling the program

In the same folder as your .nsi file and .pat files, create two blank text documents and name them FileFunc.nsh and MoveFileFolder.nsh (changing the file extension is important, so make sure not to leave them as .txt files). These are required for that macro to run properly.

Now download and install NSIS (linked above). Once it's installed, you should be able to right-click your .nsi script file and select "Compile NSIS Script". Click that, and a window will pop up detailing the compiling process. If everything went smoothly, you will see something like this at the end:

Using zlib compression.

EXE header size:                # / # bytes
Install code:                   # / # bytes
Install data:                   # / # bytes
CRC (0x1F760679):               # / # bytes

Total size:                     # / # bytes (#%)

The numbers are just little technical details, you don't need to worry about them. Just make sure there aren't any errors listed. If there are, the compiler will most likely tell you specifically which line of code is causing the problem.

All that's left is to test your installer. Run your new patcher program on the unaltered game's folder. Once the patcher is finished doing its business, close it and go play your game. Does everything look the way you want?


Resources

*ROMhacking.net
The forums here are full of very talented people who know what they're doing.

*NSIS.Sourceforge.net
Tons of documentation, plus a NSIS wiki full of tutorials. They also have forums.

This document written by SPennLUE (spennini ;at; nyit.edu) (c)2009-2010. Do not distribute unless unaltered.
Website: SPennLUE Projects